package com.demo.java.limiter;

import com.demo.java.utils.LogUtils;
import com.demo.java.utils.ThreadUtils;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 计数器 限流
 * 时间窗口内，处理请求的最大数固定，超出部分不做处理，新的时间窗口计数器置0
 *
 * 缺点：无法应对窗口连接节点的突发流量
 */
public class CounterLimiterTest {
    public static void main(String[] args) throws InterruptedException {
        CounterLimiter limiter = new CounterLimiter();
        ExecutorService executor = Executors.newFixedThreadPool(10);
        int threads = 2;
        int loop = 20;
        AtomicInteger limited = new AtomicInteger(0);
        long start = System.currentTimeMillis();

        CountDownLatch latch = new CountDownLatch(threads);


        for (int i = 0; i < threads; i++) {
            executor.submit(() -> {
                for (int j = 0; j < loop; j++) {
                    if (!limiter.tryAcquire(1)) {
                        limited.incrementAndGet();
                    }
                    ThreadUtils.sleep(200, TimeUnit.MILLISECONDS);
                }
                System.out.println(LogUtils.logt("执行结束"));
                latch.countDown();
            });
        }


        System.out.println(LogUtils.log("等待结束"));
        latch.await();

        long cost = (System.currentTimeMillis() - start)/1000;
        System.out.println(LogUtils.log("被限制次数: " + limited.get()) + ", 通过的次数: " + (threads * loop - limited.get()));
        System.out.println(LogUtils.log("限制比例: " + (limited.get()/(float)(threads * loop))));
        System.out.println(LogUtils.log("花费时间: " + cost));

        executor.shutdown();
    }

    static class CounterLimiter {
        // 窗口开始时间
        private long startTime = System.currentTimeMillis();
        // 时间窗口大小
        private int interval = 1000;
        // 时间窗口内最大处理请求数
        private int maxCount = 2;
        // 时间窗口内计数器
        private AtomicInteger counter = new AtomicInteger();

        public boolean tryAcquire(int tryCount) {
            // 当前时间
            long currentTime = System.currentTimeMillis();
            long endTime = startTime + interval;
            // 当前时间在时间窗口内，判断计数器是否超过最大
            if (currentTime <= endTime) {
                int newCount = counter.getAndIncrement();
                if (newCount <= maxCount) {
                    return true;
                } else {
                    return false;
                }
            } else {
                synchronized (CounterLimiter.class) {
                    System.out.println(LogUtils.logt("新时间窗口到了"));
                    // 再次判断，防止重复初始化
                    if (currentTime > endTime) {
                        counter.set(0);
                        startTime = currentTime;
                    }
                }
                return false;
            }
        }
    }
}
